package com.apes.scm.settle.settle.service;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.apes.framework.api.DomainService;
import com.apes.framework.jpa.spec.JpaDsl;
import com.apes.framework.plugin.esb.api.SimpleRequest;
import com.apes.framework.plugin.soe.store.database.repository.BusinessAttributeValueRepository;
import com.apes.framework.util.Arith;
import com.apes.scm.account.ledger.service.LedgerService;
import com.apes.scm.settle.settle.model.Settle;
import com.apes.scm.settle.settle.model.TaxBill;
import com.apes.scm.settle.settle.model.TaxBillItem;
import com.apes.scm.settle.settle.respository.SettleRepository;
import com.apes.scm.settle.settle.respository.TaxBillItemRepository;
import com.apes.scm.settle.settle.respository.TaxBillRepository;
import com.apestech.framework.util.StringUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.util.*;

/**
 * @Title: TaxBillService
 * @Author fj
 * @Package com.apes.scm.settle.settle.service
 * @Date 2023/10/11 13:56
 * @description:
 */
@Service("taxBillService")
public class TaxBillService extends DomainService {

    @Autowired
    private SettleRepository settleRepository;

    @Autowired
    private TaxBillRepository taxBillRepository;

    @Autowired
    private TaxBillItemRepository taxBillItemRepository;

    @Autowired
    private BusinessAttributeValueRepository businessAttributeValueRepository;

    @Autowired
    private LedgerService ledgerService;

    public static final String TYPE = "TaxBillItem.type.";

    /**
     * 新增
     * @param request
     */
    public TaxBill create(SimpleRequest request){
        JSONObject requestJO = request.getJO();
        JSONArray taxBillItems = requestJO.getJSONArray("taxBillItems");
        if (CollectionUtils.isEmpty(taxBillItems)) throw new RuntimeException("保存失败,税票信息不得为空！");

        double settleAmount = requestJO.getDoubleValue("settleAmountStr");//结算金额
        double settleTax = requestJO.getDoubleValue("settleTax");//结算税金
        double rebateAmount = requestJO.getDoubleValue("rebateAmountStr");//返利金额

        //得到结算单下所有明细数据
        List<TaxBillItem> checkList = checkList(requestJO,requestJO.getString("TAX_BILL_ID"),requestJO.getString("settleId"));

        if (CollectionUtils.isEmpty(checkList)) {
            throw new RuntimeException("保存失败,未录入有效发票信息！");
        }
        //获取累计录入发票总金额、累计税金、累计税金损失、累计折扣金额
        double amountSum = checkList.stream().mapToDouble(TaxBillItem::getAmount).sum();
        double taxAmountSum = checkList.stream().mapToDouble(TaxBillItem::getTaxAmount).sum();
        double taxLossSum = checkList.stream().mapToDouble(TaxBillItem::getTaxLoss).sum();
        double rebateAmountSum = checkList.stream().mapToDouble(TaxBillItem::getRebateAmount).sum();
        if (Arith.round(amountSum,2) > Arith.round(settleAmount,2)){
            throw new RuntimeException("保存失败,录入发票金额不得大于结算总额！");
        }
        //若发票金额和结算金额相等判断税金差
        if (Arith.round(amountSum,2) == Arith.round(settleAmount,2)){
            double poor = settleTax - (taxAmountSum + taxLossSum);
            double poorAbs = Math.abs(poor);
            if (Arith.round(poorAbs,2) > 0.5){
                throw new RuntimeException("保存失败,发票金额与结算金额相等时税金与结算税金差异不得超过0.5！");
            }
            //总折扣金额应等于返利金额
            if (Arith.round(rebateAmountSum,2) != Arith.round(rebateAmount,2)){
                throw new RuntimeException("保存失败,总折扣金额应等于返利金额");
            }
        }else if (Arith.round(taxAmountSum + taxLossSum,2) > Arith.round(settleTax+0.5,2)){
            throw new RuntimeException("保存失败,发票金额小于结算金额时税金不得超过结算税金");
        }

        //更新供应商账单和结算单已登记金额
        Settle settle = settleRepository.findOne(requestJO.getString("settleId"));
        if (!ObjectUtils.isEmpty(settle)){
            settle.setInvoicedAmount(amountSum);
            if (amountSum == settle.getSettleAmount()){
                settle.setReceiptState("already");
            }
            settleRepository.saveAndFlush(settle);
        }

        invoke("platform.settle.updateInvoicedAmount", mapper("id", requestJO.getString("platformSettleId"),"amountSum",amountSum));
        //保存
        TaxBill taxBill = new TaxBill();
        taxBill.setCompany(settle.getCompany());
        taxBill.setDept(settle.getDept());
        taxBill.setPartner(settle.getPartner());
        taxBill.setCooperation(settle.getCooperation());
        taxBill.setState("sent");
        taxBill.setSettle(settle);
        taxBill.setCreator(request.getPersonId());
        taxBill.setPaymentMonth(requestJO.getJSONObject("PLATFORM_SETTLE_ID").getString("PAYMENT_MONTH"));
        taxBill.setOrigin(requestJO.getString("platformSettleId"));
        taxBill.setOriginBusinessName("PlatformSettle");
        List<TaxBillItem> items = requestJO.getJSONArray("taxBillItems").toJavaList(TaxBillItem.class);
        taxBill.setAmtTotal(items.stream().mapToDouble(TaxBillItem::getAmount).sum());
        taxBill.setRebateAmount(items.stream().mapToDouble(TaxBillItem::getRebateAmount).sum());
        taxBill.setAmtTax(items.stream().mapToDouble(TaxBillItem::getTaxAmount).sum());
        taxBill.setAmtUntaxed(taxBill.getAmtTotal() - taxBill.getAmtTax());
        items.forEach(item -> {
            item.setTaxBill(taxBill);
            item.setSettle_id(settle.getId());
            item.setType(businessAttributeValueRepository.findOne(TYPE + item.getType().getId()));
        });
        taxBill.setTaxBillItems(new HashSet<>(items));

        TaxBill saveTaxBill = taxBillRepository.saveAndFlush(taxBill);
        //回写数据
        writeData(saveTaxBill,settle);
        return saveTaxBill;
    }

    /**
     * 得到需要校验的集合数据
     * @param requestJO
     * @return
     */
    private List<TaxBillItem> checkList(JSONObject requestJO,String taxBillId,String settleId){
        //获取已录入税票数据(编辑时需要根据结算单获取已录入的税票)
        List<TaxBillItem> taxBillItems = requestJO.getJSONArray("items").toJavaList(TaxBillItem.class);
//        if (StringUtil.isNotEmpty(taxBillId)){
//            List<TaxBillItem> items = queryTaxBillItem(settleId);
//            if (!CollectionUtils.isEmpty(items)){
//                items.forEach(item -> {
//                    if (taxBillId.equals(item.getTaxBill().getId())) return;
//                    taxBillItems.add(item);
//                });
//            }
//        }

        //本次录入税票数据
        List<TaxBillItem> items = requestJO.getJSONArray("taxBillItems").toJavaList(TaxBillItem.class);
        //将新旧数据合并校验
        List<TaxBillItem> checkList = new ArrayList<>();
        checkList.addAll(taxBillItems);
        checkList.addAll(items);
        return checkList;
    }

    /**
     * 编辑
     * @param request
     * @return
     */
    public TaxBill update(SimpleRequest request){
        JSONObject requestJO = request.getJO();
        JSONArray taxBillItems = requestJO.getJSONArray("taxBillItems");
        if (CollectionUtils.isEmpty(taxBillItems)) throw new RuntimeException("保存失败,税票信息不得为空！");

        double settleAmount = requestJO.getDoubleValue("settleAmountStr");//结算金额
        double settleTax = requestJO.getDoubleValue("settleTax");//结算税金
        double rebateAmount = requestJO.getDoubleValue("rebateAmountStr");//返利金额

        //得到结算单下所有明细数据
        List<TaxBillItem> checkList = checkList(requestJO,requestJO.getString("TAX_BILL_ID"),requestJO.getString("settleId"));

        //获取累计录入发票总金额、累计税金、累计税金损失、累计折扣金额
        double amountSum = checkList.stream().mapToDouble(TaxBillItem::getAmount).sum();
        double taxAmountSum = checkList.stream().mapToDouble(TaxBillItem::getTaxAmount).sum();
        double taxLossSum = checkList.stream().mapToDouble(TaxBillItem::getTaxLoss).sum();
        double rebateAmountSum = checkList.stream().mapToDouble(TaxBillItem::getRebateAmount).sum();
        if (Arith.round(amountSum,2) > Arith.round(settleAmount,2)){
            throw new RuntimeException("保存失败,录入发票金额不得大于结算总额！");
        }
        //若发票金额和结算金额相等判断税金差
        if (Arith.round(amountSum,2) == Arith.round(settleAmount,2)){
            double poor = settleTax - (taxAmountSum + taxLossSum);
            double poorAbs = Math.abs(poor);
            if (Arith.round(poorAbs,2) > 0.5){
                throw new RuntimeException("保存失败,发票金额与结算金额相等时税金与结算税金差异不得超过0.5！");
            }
            //总折扣金额应等于返利金额
            if (Arith.round(rebateAmountSum,2) != Arith.round(rebateAmount,2)){
                throw new RuntimeException("保存失败,总折扣金额应等于返利金额");
            }
        }else if (Arith.round(taxAmountSum + taxLossSum,2) > Arith.round(settleTax+0.5,2)){
            throw new RuntimeException("保存失败,发票金额小于结算金额时税金不得超过结算税金");
        }

        TaxBill taxBill = taxBillRepository.findOne(requestJO.getString("TAX_BILL_ID"));
        Settle settle = settleRepository.findOne(requestJO.getString("settleId"));
        //扣减总账
        deleteWriteData(taxBill,settle);
        List<TaxBillItem> newSaveList = requestJO.getJSONArray("taxBillItems").toJavaList(TaxBillItem.class);
        newSaveList.forEach(item -> {
            if (StringUtil.isEmpty(item.getSettle_id())) {
                item.setSettle_id(requestJO.getString("settleId"));
            }
            if (item.getType().getId().contains(TYPE)) {
                item.setType(businessAttributeValueRepository.findOne(item.getType().getId()));
            }else {
                item.setType(businessAttributeValueRepository.findOne(TYPE + item.getType().getId()));
            }
            if (ObjectUtils.isEmpty(item.getTaxBill())){
                item.setTaxBill(taxBill);
            }
            taxBillItemRepository.saveAndFlush(item);
        });
        //更新汇总金额
        taxBill.setAmtTotal(newSaveList.stream().mapToDouble(TaxBillItem::getAmount).sum());
        taxBill.setRebateAmount(newSaveList.stream().mapToDouble(TaxBillItem::getRebateAmount).sum());
        taxBill.setAmtTax(newSaveList.stream().mapToDouble(TaxBillItem::getTaxAmount).sum());
        taxBill.setAmtUntaxed(newSaveList.stream().mapToDouble(TaxBillItem::getAmount).sum() - newSaveList.stream().mapToDouble(TaxBillItem::getTaxAmount).sum());

        taxBill.getTaxBillItems().clear();
        taxBill.getTaxBillItems().addAll(new HashSet<>(newSaveList));
        TaxBill saveTaxBill = taxBillRepository.saveAndFlush(taxBill);
        //更新供应商账单和结算单已登记金额
        if (!ObjectUtils.isEmpty(settle)){
            settle.setInvoicedAmount(amountSum);
            if (amountSum == settle.getSettleAmount()){
                settle.setReceiptState("already");
            }
            settleRepository.saveAndFlush(settle);
        }
        invoke("platform.settle.updateInvoicedAmount", mapper("id", requestJO.getString("platformSettleId"),"amountSum",amountSum));

        //回写数据
        writeData(saveTaxBill,settle);
        return saveTaxBill;
    }

    /**
     * 审批
     * @param request
     */
    public TaxBill approve(SimpleRequest request){
        JSONObject requestJO = request.getJO();
        String id = requestJO.getString("id");
        TaxBill taxBill = taxBillRepository.findOne(id);
        if (ObjectUtils.isEmpty(taxBill)) return null;
        taxBill.setState("approve");
        taxBill.setApproveDate(new Date());
        taxBill.setApproveUID(this.getPerson(request));
        return taxBillRepository.saveAndFlush(taxBill);
    }

    /**
     * 删除
     * @param request
     */
    public String delete(SimpleRequest request){
        JSONObject requestJO = request.getJO();
        String id = requestJO.getString("id");
        TaxBill taxBill = taxBillRepository.findOne(id);
        double amountSum = taxBill.getTaxBillItems().stream().mapToDouble(TaxBillItem::getAmount).sum();
        //扣减结算单和对账单累计计算金额
        Settle settle = settleRepository.findOne(requestJO.getString("settleId"));
        if (!ObjectUtils.isEmpty(settle)){
            settle.setInvoicedAmount(settle.getInvoicedAmount() - amountSum);
            if (settle.getInvoicedAmount() == 0){
                settle.setReceiptState("not");
            }
        }
        invoke("platform.settle.updateInvoicedAmount", mapper("id", requestJO.getString("platformSettleId"),"amountSum",settle.getInvoicedAmount()));
        //删除回写总账
        deleteWriteData(taxBill,settle);
        taxBillRepository.delete(id);
        return "删除成功";
    }

    /**
     * 根据结算单id获取税票明细
     * @param request
     * @return
     */
    public Set<TaxBillItem> queryBySettleId(SimpleRequest request){
        Set<TaxBillItem> result = new HashSet<>();
        JSONObject requestJO = request.getJO();
        String id = requestJO.getString("id");
        String taxBillId = requestJO.getString("taxBillId");
        List<TaxBill> taxBills = taxBillRepository.findAll(JpaDsl.toCriteriaByEq("settle.id", id));
        if (CollectionUtils.isEmpty(taxBills)) return result;
        taxBills.forEach(item -> {
            if (item.getId().equals(taxBillId)) return;
            Set<TaxBillItem> taxBillItems = item.getTaxBillItems();
            result.addAll(taxBillItems);
        });
        return result;
    }

    /**
     * 根据结算单id获取税票明细
     * @param settleId
     * @return
     */
    public List<TaxBillItem> queryTaxBillItem(String settleId){
        List<TaxBillItem> result = new ArrayList<>();
        List<TaxBill> taxBills = taxBillRepository.findAll(JpaDsl.toCriteriaByEq("settle.id", settleId));
        if (CollectionUtils.isEmpty(taxBills)) return result;
        taxBills.forEach(item -> {
            Set<TaxBillItem> taxBillItems = item.getTaxBillItems();
            result.addAll(taxBillItems);
        });
        return result;
    }

    /**
     * 新增税票登记回写数据
     *
     * @param taxBill
     * @param settle
     */
    private void writeData(TaxBill taxBill, Settle settle) {
        if (taxBill.getAmtTotal() > 0) {
            transaction(taxBill, "current_freeze", taxBill.getAmtTotal() * -1);
            transaction(taxBill, "current_settled", taxBill.getAmtTotal());
            transaction(taxBill, "invoice_unsettled", taxBill.getAmtTotal());
        }
    }

    /**
     * 删除税票登记回写数据
     *
     * @param taxBill
     * @param settle
     */
    private void deleteWriteData(TaxBill taxBill, Settle settle) {
        if (taxBill.getAmtTotal() > 0) {
            transaction(taxBill, "current_freeze", taxBill.getAmtTotal());
            transaction(taxBill, "current_settled", taxBill.getAmtTotal() * -1);
            transaction(taxBill, "invoice_unsettled", taxBill.getAmtTotal() * -1);
        }
    }


    /**
     * 写入总账
     */
    private void transaction(TaxBill taxBill, String ledgerAccountId, double incurred) {
        ledgerService.charge(taxBill.getCompany(), taxBill.getDept(), taxBill.getPartner(),
                taxBill.getCooperation().getInventoryMode(), taxBill.getCooperation().getAccountingMethod(),
                taxBill.getId(), taxBill.getInnerBusinessType(), new Date(), ledgerAccountId, incurred);

    }
}
